-
Notifications
You must be signed in to change notification settings - Fork 0
Red-Black Tree #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Red-Black Tree #16
Conversation
src/helper/RedBlackNode.php
Outdated
|
|
||
| namespace Src\helper; | ||
|
|
||
| use Src\helper\enums\Color; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PSR4 Violation
should be
use Src\Helper\Enums\Color
Außerdem ist mir persönlich Color etwas zu generisch. Es sollte RedBlackTreeColor sein, damit es später keine Überschneidungen beim Enum Color gibt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After renaming the enums folder PHPStan was complaining that the case was wrong. Was fixed by changing it to singular.
The Helper folder should be renamed in a seperate PR since it effect the code project wide. Open Issue for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Color enum was kept generic for potential reuse later on since it could be extended without effecting the usage in the RedBlackNode/Tree implementation.
Gibt es hier ein Argument für die Spezifizierung? Allgemein wäre doch ein wiederverwendbares Color Enum sinnvoller als die Spezifizierung eine RedBlackTreeColor welches nur einen Zweck erfüllt oder?
now accepts int as input and handles parent assignment add parent property to RedBlackNode with default null to RedBlackNode
the problem arises, because the parent is set to left (null save) first and then the right is retrieved from the new parent. The previously unknown (not null save) parent is now null save but not recognised by PHPStan The same goes for the rotate left in the other direction
|



Basics 🔴 ⚫ 🌳
A red–black tree is a self-balancing binary search tree data structure, which means that the differnce between its branches is minimal. The structure is used for fast storage and retrieval of ordered information. The nodes in a red-black tree have the addition of a
color, which help with the balancing process. This happens after every node insertion and deletion and is accomplished using operations like rotation and recoloring.The code written here follows along this article: geeksforgeeks - Intorduction to Red-Black-Tree
Rules 📝
The tree follows a number of rules which are:
Operations ⚒️
The red-black-tree (rbt) consists of red-black-nodes (rbn) and should have the following operations:
The insertion will hapen on the rbt level, so will the traversal. The Rotation on the other hand will happen around a node and therefore will be implemented as method on the node.
Insertion
The insertion starts of with the basics of a binary tree. If the root is not set, declare the new node as the root and we are done. Since the second rule states, that the root has to be black, change the color and we are done for the rbt too.
Adding more elements to the 0️⃣1️⃣🌳
When the root is already set and new elements are added, we walk the tree node by node and decide if we travel to the left or the right by comparing the value of the current node with the value for the new node we want to insert.
Should the child we would travel to be a leaf (a nil node respectively a null RedBlackNode), we write the value to a new node as the child in the travel direction and stop the walk.
Error Correction (balancing) 🔧🛠️👨🔧
When a new Node is added we check against a list of criteria to validate, that the rules of the rbt are not violated. This happens in the following steps:
Happy paths 😊
Red uncel ⬆️⬆️⬇️🔴
If the uncle (sibling of parent) is red -> change color of parent and uncle to black -> change color of grandparent to red -> procede higher up in the tree
Black uncel ⬆️⬆️⬇️⚫
In the case of a black uncle thre are a few more subconditions that decide about the further logic. The conditions are that the node:
The rotation can happen in two different way. The following showcases make the rotation around the node
xand do not take into account, that the parents of x need to be updated to:➡️ to the right
graph TD %% Define Styles classDef unchanged fill:grey; %% After Rotation subgraph "After Right Rotation" Y2["y"] A2["a"] X2["x"] B2["b"] C2["c"] Y2 --> A2 Y2 --> X2 X2 --> B2 X2 --> C2 class A2 unchanged class C2 unchanged end %% Before Rotation subgraph "Before Right Rotation" X["x"] Y["y"] A["a"] B["b"] C["c"] X --> Y X --> C Y --> A Y --> B class A unchanged class C unchanged end %% Arrow styles linkStyle 0 stroke:grey,stroke-width:2px; linkStyle 1 stroke:black,stroke-width:2px; linkStyle 2 stroke:black,stroke-width:2px; linkStyle 3 stroke:grey,stroke-width:2px; linkStyle 4 stroke:black,stroke-width:2px; linkStyle 5 stroke:grey,stroke-width:2px; linkStyle 6 stroke:grey,stroke-width:2px; linkStyle 7 stroke:black,stroke-width:2px;⬅️ to the left
graph TD %% Define Styles classDef unchanged fill:grey; %% After Rotation subgraph "After Left Rotation" Y2["y"] X2["x"] A2["a"] B2["b"] C2["c"] Y2 --> X2 Y2 --> C2 X2 --> A2 X2 --> B2 class A2 unchanged class C2 unchanged end %% Before Rotation subgraph "Before Left Rotation" X["x"] Y["y"] A["a"] B["b"] C["c"] X --> A X --> Y Y --> B Y --> C class A unchanged class C unchanged end %% Arrow styles linkStyle 0 stroke:black,stroke-width:2px; linkStyle 1 stroke:black,stroke-width:2px; linkStyle 2 stroke:grey,stroke-width:2px; linkStyle 3 stroke:black,stroke-width:2px; linkStyle 4 stroke:grey,stroke-width:2px; linkStyle 5 stroke:black,stroke-width:2px; linkStyle 6 stroke:black,stroke-width:2px; linkStyle 7 stroke:grey,stroke-width:2px;right child of a left child (or vice versa)
Depending on the condition the subtree is rotated agains the direction of the node around it's parent.
left child of a left child (or right of a right)
Depending on the direction of the new node, the rotations happens around the direction of the node around the grandparent befor fixing the coloring of the then rearanged subtree.
🏗️💪🔩 Refactoring
Refactoring is fun and makes the code much cleaner. This was made simpler by adding test for the functionality. After the test where added and some bug out the window, the main refactoring could happen:
$leftand$rightattributes where extended by an update of the parent and later on where extend to handle null as parameter which further reduced the complexity of the coderotateLeftandrotateRightmethods both start of basicly the same, by checking if the parent of the center exists and update it's appropriate child and the parent of the corresponding child, the logic was also extracted and abstracted by providing it with the direction in which the child should be taken. Later on the syntax was slimmed down further by taking into account, that a cild which is neither the right nor the left sibling child, has no parent1️⃣2️⃣2️⃣ Enum
Since the idea of a color enum worked out fine and i was confident in the tests, i decided to go a step further and implement a direction emun.
The enum consists of
leftandrightand provides them in uppercase as name and in lowercase as value. This enabled an additional refactoring, removin duplication for rotations, retrieval of sibling and dubplications for directional child checks.Deletion
The deletion follows a similar strucutre. We first performe a basic binary tree deletion and then rebalance/fix the result.
Standart Binary Tree deletion
The standard deletion in a Binary tree has 3 scenarios which look like this: